
#include <iostream>

#include <sys/time.h>
#include <time.h>

#include "common.h"
#include "SDL_GLApp.h"
#include "LogUtil.h"

using namespace std;

__BEGIN_NAMESPACE_DECL_

static void timestamp(const char* format , ...)
{
    struct timeval tv;

    gettimeofday (&tv, NULL);

    va_list args;
    va_start(args,format);
    printf("%ld.%ld: ", tv.tv_sec, tv.tv_usec);
    vprintf(format,args);
    va_end(args);
}

// Helper function to initialize member variables
void
SDL_GLApp::init()
{
    screen_ = NULL;
    frameCount_ = 0;
    done_ = false;
    fullscreen_ = false;
    fsaa_ = false;
    fsaaSamples_ = 2;
    fps_ = 0.0;
}

// Default Constructor
SDL_GLApp::SDL_GLApp ()
{
    width_ = 800;
    height_ = 600;
    title_ = "Window Title";
    init();
}

SDL_GLApp::SDL_GLApp (const char *title)
    : title_(title)
{
    width_ = 800;
    height_ = 600;
    init();
}

SDL_GLApp::SDL_GLApp (const char *title, int width, int height)
    : title_(title), width_(width), height_(height)
{
    init();
}


// Destructor 
SDL_GLApp::~SDL_GLApp ()
{
    if (timer != NULL)
    {
        if (!SDL_RemoveTimer(timer))
        {
            WARN << "Error removing timer." << endl;
        }
    }
}

void
SDL_GLApp::InitApp ()
{
    /* Initialize the SDL library */
    if (SDL_Init(SDL_INIT_EVERYTHING) < 0)
    {
        cerr << "There was an error initializing SDL: " << SDL_GetError();
        SDL_Quit();
    }

    // Use an RGB color scheme (as opposed to indexed color)
    // Also, enable double buffering.
    SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
    SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);
    SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
    SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
    SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
    SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
   
    if (fsaa_)
    {
        SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
        SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, fsaaSamples_);
    }

    // Create a drawing context in SDL: this also creates a window
    InitVideoMode();

    if (screen_ && fsaa_)
    {
        int buffers, samples ;
        SDL_GL_GetAttribute(SDL_GL_MULTISAMPLEBUFFERS, &buffers);
        SDL_GL_GetAttribute(SDL_GL_MULTISAMPLESAMPLES, &samples);
   
        if (buffers == 0 || samples == 0)
        {
            WARN << "Unable to turn on FSAA/Multisampling";
            fsaa_ = false;
        }
        else 
            DEBUG << "FSAA/multisampling enabled: buffers="
                  << buffers << ", samples=" << samples;
    }

    // Set the window title
    SDL_WM_SetCaption (title_, title_);
}

void
SDL_GLApp::Display()
{
    // default implementation is to draw a blank screen
    glClearColor (0.0, 0.0, 0.0, 0.0);
    glClear(GL_COLOR_BUFFER_BIT);

    SDL_GL_SwapBuffers();
}

void
SDL_GLApp::Process()
{
    // default implementation is simply as for scene to be displayed again
    RequestRedisplay();
}

void
SDL_GLApp::Resize(int width, int height)
{
    width_ = width;
    height_ = height; 

    // Have to reinitialize the video mode
    InitVideoMode();
}

void
SDL_GLApp::Keyboard(const SDLKey& key)
{
    // empty
}

void 
SDL_GLApp::MouseMotion(Uint8 state, Uint16 x, Uint16 y)
{
}

void
SDL_GLApp::Expose()
{
    RequestRedisplay();
}

void
SDL_GLApp::RequestRedisplay()
{
    SDL_Event event;

    /**
     * Construct a user event with the type USER_EVENT_REDISPLAY and
     * push it on to the event queue.  The main loop should honor
     * the request as soon as it finishes processing events by calling
     * the Display() member function.
     */
    event.type = SDL_USEREVENT;
    event.user.code = USER_EVENT_REDISPLAY;
    event.user.data1 = 0;
    event.user.data2 = 0;

    SDL_PushEvent(&event);

    return;
}

void
SDL_GLApp::Start()
{
    SDL_Event event;

    // Render the scene once before starting to process main loop events
    Resize(width_, height_);
    Display();

    // Set the timer in motion to call the Process() callback
    timer = SDL_AddTimer (10, ProcessTimer, this);

    Uint32 lastTicks = SDL_GetTicks();

    // Main loop
    while (!done_)
    {
        Uint32 ticks = SDL_GetTicks();
        if ((ticks-lastTicks) >= 2000) 
        {
            double elapsedTime = (double)(ticks-lastTicks)/1000;
            fps_ = (double)frameCount_ / elapsedTime;
            if (LogUtil::Level() == LEVEL_DEBUG)
                printf ("[%d]FPS %.4f in %.4f seconds\n", frameCount_, fps_, elapsedTime);
            lastTicks = ticks;
            frameCount_ = 0;
        }
        bool render = false;

        // Process all events before rendering
        while (SDL_PollEvent(&event)) 
        {
            switch (event.type)
            {
                case SDL_USEREVENT:
                    //printf ("User event occurred: %x\n", event.user.code);
                    if (event.user.code == USER_EVENT_REDISPLAY)
                    {
                        //timestamp("Redisplay Event\n");
                        render = true;
                    }
                    else if (event.user.code == USER_EVENT_PROCESS)
                    {
                        //timestamp("Process Event\n");
                        Process();
                    }
                    break;
                case SDL_VIDEORESIZE:
                    //timestamp("Resize Event\n");
                    Resize (event.resize.w, event.resize.h);
                    break;
                case SDL_VIDEOEXPOSE:
                    //timestamp("Expose Event\n");
                    Expose();
                    break;
                case SDL_KEYDOWN:
                    //timestamp("Key is down: %x\n", event.key.keysym.sym);
                    Keyboard(event.key.keysym.sym);
                    break;
                case SDL_MOUSEMOTION:
                    MouseMotion(event.motion.state, event.motion.x, event.motion.y);
                    break;
                case SDL_MOUSEBUTTONDOWN:
                    //timestamp("Mouse is down\n");
                    break;
                case SDL_QUIT:
                    timestamp("Done\n");
                    done_ = true;
                    break;
                default:
                    break;
            }
        }

        //if (render)
            Display();

        // Call the idle function
        //Idle();
    }
}

void
SDL_GLApp::setFullscreen (bool fullscreen)
{
    // XXX: may need to reinitialize video mode here
    // if we've already initialized the video mode
    fullscreen_ = fullscreen;
}

bool
SDL_GLApp::isFullscreen () const
{
    return fullscreen_;
}

// FSAA
void SDL_GLApp::setFSAA (bool fsaa) { fsaa_ = fsaa; }
bool SDL_GLApp::isFSAA () const { return fsaa_; }

void SDL_GLApp::setSamplesFSAA (int samples)
{
    if (samples == 1 || samples == 2 || 
        samples == 4 || samples == 8 || samples == 16)
    {
        fsaaSamples_ = samples;
    }
    else
    {
        fsaaSamples_ = 2;
        WARN << "FSAA Samples must be one of 1,2,4,8 or 16";
        WARN << "Defaulting # FSAA samples = "<<fsaaSamples_;
    }
}

int SDL_GLApp::getSamplesFSAA () const
{
    return fsaaSamples_;
}


Uint32
SDL_GLApp::ProcessTimer (Uint32 interval, void *param)
{
    SDL_Event event;

    event.type = SDL_USEREVENT;
    event.user.code = USER_EVENT_PROCESS;
    event.user.data1 = 0;
    event.user.data2 = 0;

    SDL_PushEvent(&event);

    return interval;
}

void
SDL_GLApp::InitVideoMode ()
{
    Uint32 flags = SDL_OPENGL | SDL_RESIZABLE | SDL_SRCALPHA;
    flags |= (fullscreen_) ? SDL_FULLSCREEN : 0;

    int width = (fullscreen_) ? 0 : width_;
    int height = (fullscreen_) ? 0 : height_;

    //Uint32 flags = SDL_OPENGL | SDL_RESIZABLE | SDL_OPENGLBLIT | SDL_SRCALPHA;
    if ((screen_ = SDL_SetVideoMode(width, height, 0, flags)) == NULL)
    {
        fprintf(stderr, "There was an error setting the video mode: %s\n", 
                SDL_GetError());
        SDL_Quit();
        exit(EXIT_FAILURE);
    }

    // if fullscreen mode was requested, get the width and height
    if (screen_ && fullscreen_)
    {
        width_ = screen_->w;
        height_ = screen_->h;
    }
}


__END_NAMESPACE_DECL_
